﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.ComplexEventProcessing;
using Microsoft.ComplexEventProcessing.Adapters;
using Microsoft.Samples.SmartGrid.AMI.MeterEventType;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Threading;

namespace Microsoft.Samples.SmartGrid.AMI.WcfInputAdapter
{
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, IncludeExceptionDetailInFaults=true)]
    public class WcfInputPointAdapter:PointInputAdapter, IMeterEventType
    {
        private WcfInputAdapterConfig config;
        private static readonly int StopPollingPeriod = 1000; //msec
        private static int ctiEventEnqueuePeriod = 0; //msec
        private readonly Timer timer;
        private readonly Timer ctiTimer;
        private readonly object sync;
        private readonly ServiceHost host;

        public WcfInputPointAdapter(WcfInputAdapterConfig config)
        {
            this.config = config;
            this.sync = new object();
            this.host = new ServiceHost(this);
            host.AddServiceEndpoint(typeof(IMeterEventType), new BasicHttpBinding(), config.BaseAddress);
            ServiceMetadataBehavior smdb = new ServiceMetadataBehavior();
            smdb.HttpGetEnabled = true;
            smdb.HttpGetUrl = config.BaseAddress;
            host.Description.Behaviors.Add(smdb);

            //poll the adapter to determine when it is time to stop.
            this.timer = new Timer(CheckStopping);
            this.timer.Change(StopPollingPeriod, Timeout.Infinite);

            //Submit a CTI Heartbeat
            ctiEventEnqueuePeriod = config.ExplicitCtiPublishingFrequency;
            this.ctiTimer = new Timer(SubmitCTIEvent);
            this.ctiTimer.Change(ctiEventEnqueuePeriod, Timeout.Infinite);

        }


        private void SubmitCTIEvent(object state)
        {
            lock (this.sync)
            {
                if (AdapterState != AdapterState.Stopping)
                {
                    EnqueueCtiEvent(DateTime.Now);
                    this.ctiTimer.Change(ctiEventEnqueuePeriod, Timeout.Infinite);
                }
            }
        }

        private void CheckStopping(object state)
        {
            lock (this.sync)
            {
                if (AdapterState == AdapterState.Stopping)
                {
                    this.host.Close();
                    Stopped();
                }
                else
                {
                    this.timer.Change(StopPollingPeriod, Timeout.Infinite);
                }
            }
        }

        public void SubmitMeterEvent(RawMeterEventType rawMeterEventType)
        {
            PointEvent pe = base.CreateInsertEvent();
            pe.StartTime = DateTime.Now;

            pe.SetField(0, rawMeterEventType.DataCurrentReading);
            pe.SetField(1, rawMeterEventType.DataCurrentTemp);
            pe.SetField(2, rawMeterEventType.EventTimeStamp);
            pe.SetField(3, rawMeterEventType.EventType);
            pe.SetField(4, rawMeterEventType.MeterHeadEndID);
            pe.SetField(5, rawMeterEventType.MeterNumber);
            pe.SetField(6, rawMeterEventType.MeterVendorID);

            //not checking to see if Queue is full as result code
            Enqueue(ref pe);

            Console.WriteLine("     submitted: " + rawMeterEventType.MeterNumber + " " + rawMeterEventType.MeterHeadEndID + " " + rawMeterEventType.MeterVendorID);
        }

        public override void Resume()
        {
            //Not Implemented
        }

        public override void Start()
        {
            this.host.Open();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                ((IDisposable)this.host).Dispose();
            }
            base.Dispose(disposing);
        }



    }

    [ServiceContract]
    public interface IMeterEventType
    {
        [OperationContract]
        void SubmitMeterEvent(RawMeterEventType  rawMeterEventType);

    }
}

